package com.ijmacd.footballtracker;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

import com.ijmacd.footballtracker.Provider.Matches;
import com.ijmacd.footballtracker.Provider.Teams;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentValues;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.CompressFormat;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;

public class DataFetcher extends Service {
	
	private static final String TAG = "DataFetcher";
	
	private static final String PREFS_NAME = "DataPrefs";
	private static final String PREFKEY_TEAMS_UPDATED = "teams_updated";
	private static final String PREFKEY_MATCHES_UPDATED = "matches_updated";
	
	private static final String QUERY_URL = "http://footballtracker.co.cc/query.php";
	private static final int XML_VERSION = 1;

	private NotificationManager mNM;
	private Looper mServiceLooper;
	private ServiceHandler mServiceHandler;
    private SharedPreferences mSettings;

    private final Query qMatch = new Query("match");
    
	private final ArrayList<Query> mQueryQueue = new ArrayList<Query>();

	private final ArrayList<ContentValues> mMatchValues = new ArrayList<ContentValues>();
	private final ArrayList<ContentValues> mTeamValues = new ArrayList<ContentValues>();

	
	private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message msg)
        {
            showNotification("Downloading Data");

            //mQueryQueue.add(qMatch.setSince(mSettings.getInt(PREFKEY_MATCHES_UPDATED, 0)));
            runQueries();

            hideNotification();
            
            sendMessageDelayed(obtainMessage(), 150000);
        }

    };
	@Override
	public void onCreate() {
		Log.i(TAG, "Service Starting");

		mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
		mSettings = getSharedPreferences(PREFS_NAME, 0);
		
		Toast.makeText(this, R.string.service_started, Toast.LENGTH_SHORT).show();

        HandlerThread thread = new HandlerThread("ServiceStartArguments", 3);
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
	}
	
	@Override
	public void onStart(Intent intent, int startId) {
        Log.i(TAG, "Starting #" + startId + ": " + intent.getExtras());
	    Message msg = mServiceHandler.obtainMessage();
	    msg.obj = intent.getExtras();
	    mQueryQueue.add(qMatch);
	    mServiceHandler.sendMessage(msg);
	    Log.i("ServiceStartArguments", "Sending: " + msg);
	}

	@Override
	public void onDestroy() {
		mServiceLooper.quit();
		
		hideNotification();

		Toast.makeText(this, R.string.service_stopped, Toast.LENGTH_SHORT).show();
	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}
	
	private void runQueries(){
        Document doc = null;
        StringBuilder query_string = new StringBuilder();
        if(mQueryQueue.size() == 0) return;
        if(mQueryQueue.size() == 1){
        	query_string.append(mQueryQueue.get(0));
        }else{
	        
        	int i = 0;
	        //for(int i = 0; i < mQueryQueue.size(); i++){
        	while(mQueryQueue.size() > 0){
	        	if(i >= 20) break;
	        	if(i != 0) query_string.append("&");
	        	query_string.append(mQueryQueue.get(0).toString(i));
	        	mQueryQueue.remove(0);
	        	i++;
	        }
        }
        //Query[] queue_copy = listToArray(mQueryQueue);
		String uri = QUERY_URL + "?" + query_string;
		Log.d(TAG, "URI: " + uri);
		try {
			doc = new SAXBuilder().build(URI.create(uri).toURL());
			if(doc.getRootElement() == null || doc.getRootElement().getAttribute("version") == null){
				Log.e(TAG, "Unexpected response");
				//Log.e(TAG, new XMLOutputter().outputString(doc.getRootElement()));
				return;
			}
			int version = doc.getRootElement().getAttribute("version").getIntValue();
			if(version > XML_VERSION){
				Log.e(TAG, "Unexpected version of reply from server");
				return;
			}
			List queries = doc.getRootElement().getChildren("query");
			if(queries.size() < 1){
				Log.e(TAG, "Unexpected response");
				return;
			}
		    SharedPreferences.Editor editor = mSettings.edit();
			int qNo = 0;
			for(Object _query: queries){
    			if(_query.getClass() != Element.class) continue;
    			Element result = ((Element)_query).getChild("result");
    			if(result == null){
    				Log.e(TAG, "Query " + qNo + ": Unexpected response");
    				return;
    			}
    			int result_count = result.getAttribute("count").getIntValue();
    			if(result_count < 1){
    				Log.i(TAG, "Query " + qNo + ": No results were found for this query");
    			}else{
	    			int result_num = result.getChildren().size();
	        		if(result_count != result_num){
	        			Log.w(TAG, "Query " + qNo + ": The reported number of results does not match the number found");
	        		}
	        		Log.v(TAG, "Query " + qNo + ": Found " + result_num + " (" + result_count + " reported) results");
	        		for(Object _element: result.getChildren()){
	        			if(_element.getClass() != Element.class) continue;
	        			Element element = (Element)_element;
	        			if(element.getName().equals("match")) parseMatch(element);
	        			else if(element.getName().equals("team")) parseTeam(element);
	        		}
    			}
	        	Element request = ((Element)_query).getChild("request");
		        int time = request.getAttribute("timestamp").getIntValue();
	        	//if(queue_copy[qNo].Request.equals("match") && queue_copy[qNo].uuid.equals("")){
		        if(request.getAttributeValue("value").equals("match") && !uri.contains("uuid" + qNo)){
		            editor.putInt(PREFKEY_MATCHES_UPDATED, time);
	        	//}else if(queue_copy[qNo].Request.equals("team") && queue_copy[qNo].uuid.equals("")){
	        	}else if(request.getAttributeValue("value").equals("team") && !uri.contains("uuid" + qNo)  && !uri.contains("name" + qNo)){
		            editor.putInt(PREFKEY_TEAMS_UPDATED, time);
	        	}
		        qNo++;
			}
		    editor.commit();
    		postResults();
			if(mQueryQueue.size() > 0) mServiceHandler.dispatchMessage(mServiceHandler.obtainMessage());
		} catch (MalformedURLException e) {
			Log.e(TAG, "Problem with Query URL");
		} catch (JDOMException e) {
			Log.e(TAG, "Invalid XML");
		} catch (IOException e) {
			Log.e(TAG, "Error handling the data");
		}

	}
	/**
	 * Make sure you call postResults() afterwards
	 * @param match the jdom xml Element describing the match
	 */
	private void parseMatch(Element match) {
		ContentValues values = new ContentValues();
		String uuid = match.getAttributeValue("uuid");
        if(uuid.substring(0, 9).equals("urn:uuid:")) uuid = uuid.substring(9);
        values.put(Matches._ID, uuid);
        if(match.getChild("home") != null){
        	if(match.getChild("home").getChild("team") != null){
        		Element team = match.getChild("home").getChild("team");
        		parseTeam(team);
            	String home_uuid = team.getAttributeValue("uuid");
                if(home_uuid.substring(0, 9).equals("urn:uuid:")) home_uuid = home_uuid.substring(9);
                values.put(Matches.HOME_TEAM, home_uuid);
        	}
            values.put(Matches.HOME_GOALS, match.getChild("home").getChildText("goals"));
        }
        if(match.getChild("away") != null){
        	if(match.getChild("away").getChild("team") != null){
        		Element team = match.getChild("away").getChild("team");
        		parseTeam(team);
            	String away_uuid = team.getAttributeValue("uuid");
                if(away_uuid.substring(0, 9).equals("urn:uuid:")) away_uuid = away_uuid.substring(9);
                values.put(Matches.AWAY_TEAM, away_uuid);
        	}
            values.put(Matches.AWAY_GOALS, match.getChild("away").getChildText("goals"));
        }
        values.put(Matches.DATE, match.getChildText("date"));
        values.put(Matches.STATUS, match.getChildText("status"));
        if(match.getChildText("attendance") != null){
        	values.put(Matches.ATTENDANCE, match.getChildText("attendance"));
        }
        if(values.size() > 0) mMatchValues.add(values);
	}

	/**
	 * Make sure you call postResults() afterwards
	 * @param team the jdom xml Element describing the team
	 */
	private void parseTeam(Element team) {
		String uuid = team.getAttributeValue("uuid");
	    if(uuid.substring(0, 9).equals("urn:uuid:")) uuid = uuid.substring(9);
	    if(team.getChildren().size() == 0){
	    	mQueryQueue.add(new Query("team").setUuid(uuid));
	    }else{
			ContentValues values = new ContentValues();
		    values.put(Teams._ID, uuid);
		    values.put(Teams.NAME, team.getChildText("name"));
		    if(team.getChildText("logo_small") != null){
				try {
					URL url = new URL(team.getChildText("logo_small"));
			    	File file = new File(getCacheDir().getAbsolutePath() + "/logo_small/" + uuid);
					file.getParentFile().mkdirs();
					if(file.getParentFile().exists() && !file.exists()){
			        	Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());
			        	bitmap.compress(CompressFormat.PNG, 100, new FileOutputStream(file));
			        	values.put(Teams.LOGO_SMALL, file.getAbsolutePath());
			        }
				} catch (MalformedURLException e) {
					Log.i(TAG, "Invalid URL retrieved for logo");
				} catch (IOException e) {
					Log.w(TAG, "Unable to download logo");
				}
		    }
		    if(values.size() > 0) mTeamValues.add(values);
	    }
	}


	private void postResults() {
		if(mMatchValues.size() > 0){
			Log.d(TAG, "Posting match results to content provider");
			getContentResolver().bulkInsert(Provider.Matches.CONTENT_URI, listToArray(mMatchValues));
		}
		if(mTeamValues.size() > 0){
			Log.d(TAG, "Posting team results to content provider");
			getContentResolver().bulkInsert(Provider.Teams.CONTENT_URI, listToArray(mTeamValues));
		}
		mMatchValues.clear();
		mTeamValues.clear();
	}

	private void showNotification(String txt) {
		
		Notification n = new Notification(R.drawable.notball, txt, System.currentTimeMillis());
		
		PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, FootballTracker.class), 0);
		
		n.setLatestEventInfo(this, getText(R.string.service_tracker), txt, contentIntent);
		
		n.flags += Notification.FLAG_ONGOING_EVENT;
		
		mNM.notify(R.string.service_started, n);
		
	}
	
    private void hideNotification() {
        mNM.cancel(R.string.service_started);
    }

    private ContentValues[] listToArray(ArrayList<ContentValues> list){
    	ContentValues[] out = new ContentValues[list.size()];
    	for(int i = 0; i < out.length; i++) out[i] = list.get(i);
    	return out;
    }
    private Query[] listToArray(ArrayList<Query> list){
    	Query[] out = new Query[list.size()];
    	for(int i = 0; i < out.length; i++) out[i] = list.get(i);
    	return out;
    }
    private <T>T[] listToArray(ArrayList<T> list){
    	T[] out = (T[]) new Object[list.size()];
    	for(int i = 0; i < out.length; i++) out[i] = list.get(i);
    	return out;
    }
	
	private class Query
	{
		public String Request;
		public String Select;
		public String uuid;
		public int Since = -1;
		public int Limit = -1;

		Query(String request){ this.Request = request; }
		Query(String request, String select){ this.Request = request; this.Select = select; }	
		Query setUuid(String uuid){ this.uuid = uuid; return this; }
		Query setSince(int since){ this.Since = since; return this; }	
		Query setLimit(int limit){ this.Limit = limit; return this; }

		public String toString(){
			return "request=" + Request
				+ ((Select != null) ? "&select=" + Select: "")
				+ ((uuid   != null) ? "&uuid=" + uuid : "")
				+ ((Since != -1) ? "&since=" + Since : "")
				+ ((Limit != -1) ? "&limit=" + Limit : "&limit=no");
		}
		public String toString(int i){
			return "request" + i + "=" + Request
				+ ((Select != null) ? "&select" + i + "=" + Select: "")
				+ ((uuid   != null) ? "&uuid" + i + "=" + uuid : "")
				+ ((Since != -1) ? "&since" + i + "=" + Since : "")
				+ ((Limit != -1) ? "&limit" + i + "=" + Limit : "&limit" + i + "=no");
		}
	}
}
